/**
 * 俄罗斯方块游戏
 * 
 * 对象分析：
 * 1. 方块组
 *    属性：形状描述，坐标，颜色
 *    方法：移动、旋转
 *    有 7 种形状: S, Z, I, O, J, L, T
 *    有 7 种颜色: lime, red, cyan, yellow, blue, orange, purple
 * 2. 游戏
 *    属性：背景色，当前的方块，
 *    方法：开始游戏、暂停游戏、结束游戏、
 * 
 */

const { random, floor } = Math,
  randInt = (max, min = 0) => floor(random() * (max - min)) + min,
  randItem = (...args) => args[randInt(args.length)],
  max = (arr) => arr.reduce((p, c) => p > c ? p : c),
  min = (arr) => arr.reduce((p, c) => p < c ? p : c);


/**
 * Tetris 所有俄罗斯方块的基类
 * 
 * 属性：color, shape, position
 * 方法：move, rotate
 */
class Tetris {
  // 颜色
  color = '';
  // 形状：方块的位置
  shape = [];
  // 位置: 以左上角为原点，x 轴为 0 开始，y 轴为 0 开始
  position = { x: 3, y: 0 }

  // 只读属性
  get minX() {
    return min(this.shape.map(([x]) => x));
  }
  get maxX() {
    return max(this.shape.map(([x]) => x));
  }
  // 
  get minY() {
    return min(this.shape.map(([, y]) => y));
  }
  get maxY() {
    return max(this.shape.map(([, y]) => y));
  }
  // 宽度
  get width() {
    return this.maxX - this.minX + 1;
  }
  // 高度
  get height() {
    return this.maxY - this.minY + 1;
  }

  // 是否发生碰撞
  isCollision(x, y) {
    const { width, height } = this;
    if (x < 0 || x - width > 9 || y - height > 19) return true;
    // 通过碰撞检测
    return false;
  }

  /**
   * move 移动方块
   * 
   * @param { 'left' | 'right' | 'down' } direction 移动的方向
   */
  move(direction) {
    const { x, y } = this.position;
    if (direction === 'left') {
      if (!this.isCollision(x - 1, y))
        this.position.x -= 1;
    } else if (direction === 'right') {
      if (!this.isCollision(x + 1, y))
        this.position.x += 1;
    } else if (direction === 'down') {
      if (!this.isCollision(x, y + 1))
        this.position.y += 1;
    }
  }

  /**
   * 旋转方块
   * 
   * @param { 'left' | 'right' } direction 旋转的方向
   */
  rotate(direction) {
    // 旋转的规则：
    // 左旋：x1 = x * con(a) - y * sin(a),  y1 = x * sin(a) + y * con(a)
    // 右旋：x1 = x * con(a) + y * sin(a),  y1 = -x * sin(a) + y * con(a)
    // 由于 a 是 90 度，所以 sin(a) = 1, con(a) = 0
    // 左旋：x1 = y, y1 = -x
    // 右旋：x1 = -y, y1 = x
    const shape = direction == 'left'
      ? this.shape.map(([x, y]) => [y, -x])
      : this.shape.map(([x, y]) => [-y, x]);
    // 测试所有点都不在碰撞区域内，则旋转成功！
    if (shape.every(([x, y]) => !this.isCollision(x + this.position.x, y + this.position.y))) {
      this.shape = shape;
    }
  }
  *[Symbol.iterator]() {
    for (let [x, y] of this.shape) {
      yield [this.position.x + x, this.position.y + y]
    }
  }

}

/**
 * I 俄罗斯方块 Tetris 的子类
 * 
 * 继承 Tetris 类的属性和方法
 */
class I extends Tetris {
  // 颜色
  color = 'cyan';
  // 形状：方块的位置
  shape = [[-1, 0], [0, 0], [1, 0], [2, 0]];
  // 位置: 以左上角为原点，x 轴为 0 开始，y 轴为 0 开始
  position = { x: 3, y: 0 }
}

class S extends Tetris {
  // 颜色
  color = 'lime';
  // 形状：方块的位置
  shape = [[0, 0], [1, 0], [-1, 1], [0, 1]];
  // 位置: 以左上角为原点，x 轴为 0 开始，y 轴为 0 开始
  position = { x: 4, y: 0 }
}

class Z extends Tetris {
  color = 'red';
  shape = [[-1, 0], [0, 0], [0, 1], [1, 1]];
  position = { x: 4, y: 0 }
}

class O extends Tetris {
  color = 'yellow';
  shape = [[-1, 0], [0, 0], [-1, 1], [0, 1]];
  position = { x: 4, y: 0 }

  rotate() {
    // 不旋转 
  }
}

class J extends Tetris {
  color = 'blue';
  shape = [[-1, -1], [-1, 0], [0, 0], [1, 0]];
  position = { x: 4, y: 0 }
}

class L extends Tetris {
  color = 'orange';
  shape = [[-1, 0], [0, 0], [1, 0], [1, -1]];
  position = { x: 4, y: 0 }
}

class T extends Tetris {
  color = 'purple';
  shape = [[-1, 0], [0, 0], [1, 0], [0, -1]];
  position = { x: 4, y: 0 }
}

/**
 * 游戏类
 * 
 * 属性：background, curent, next
 * 方法：start, pause
 */
class Game {
  constructor(container) {
    // 游戏背景
    this.background = new Array(20).fill(new Array(10).fill('black'));
    // 当前的方块
    this.current = new (randItem(I, Z, S, O, J, L, T))();
    // 方块的边长
    this.size = 40;
    // 动态创建 canvas 元素
    const canvas = document.createElement('canvas');
    canvas.width = 10 * this.size;
    canvas.height = 20 * this.size;
    canvas.style.border = '1px solid black';
    container.appendChild(canvas);
    // 获取 canvas 的上下文
    this.ctx = canvas.getContext('2d');
  }

  start() {
    // 绘制游戏第一帧
    this.draw();
    // 监听键盘事件
    window.addEventListener('keydown', (e) => {
      if (e.code === 'ArrowLeft') {
        this.current.move('left');
      } else if (e.code === 'ArrowRight') {
        this.current.move('right');
      } else if (e.code === 'ArrowDown') {
        this.current.move('down');
      } else if (e.code === 'ArrowUp') {
        this.current.rotate('left');
      }
    })


  }

  /**
   * 绘制游戏帧
   * 
   * 只绘制当前方块和背景的一帧
   */
  draw() {
    const { ctx, size } = this;
    // 绘制黑色背景
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, 10 * size, 20 * size);
    // 绘制参考线





    for (let i = 1; i < 20; i++) {
      ctx.moveTo(0, size * i);
      ctx.lineTo(10 * size, size * i);
      ctx.strokeStyle = 'white';
      ctx.lineWidth = 1;
      ctx.stroke();
    }
    for (let i = 1; i < 10; i++) {
      ctx.moveTo(size * i, 0);
      ctx.lineTo(size * i, 20 * size);
      ctx.strokeStyle = 'white';
      ctx.lineWidth = 1;
      ctx.stroke();
    }
    // 绘制当前方块

    const { color, shape, position } = this.current;
    ctx.fillStyle = color;
    shape.forEach(([x, y]) => {
      ctx.fillRect(position.x * size + x * size, position.y * size + y * size, size, size);
    });

    // 绘制下一帧
    requestAnimationFrame(() => this.draw());
  }
}